package com.supermap.fabricsdk;


import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.helper.Config;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
import org.hyperledger.fabric.sdk.security.gm.SM2Utils;
import org.hyperledger.fabric_ca.sdk.HFCAClient;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * SmChain 客户端，基于 HFClient 封装。是操作区块链的主入口
 */
public class SmChain {

    private static Logger logger = Logger.getLogger(SmChain.class.toString());
    public HFClient hfClient;
    private Channel channel;
    private NetworkConfig networkConfig;
    private String grpcTypeName = "grpc.NettyChannelBuilderOption.maxInboundMessageSize";
    private int grpcMaxSize = 100 * 1024 * 1024;

    public enum CryptoSuiteChoose {
        ECDSA,
        GM
    }

    private void setNetworkConfig(){
        // peer grpc size = 100M
        String peername = networkConfig.getPeerNames().iterator().next();
        Properties peerProperties = networkConfig.getPeerProperties(peername);
        peerProperties.put(grpcTypeName, grpcMaxSize);
        networkConfig.setPeerProperties(peername, peerProperties);
        // orderer grps size = 100M
        String orderername = networkConfig.getOrdererNames().iterator().next();
        Properties ordererProperties = networkConfig.getOrdererProperties(orderername);
        ordererProperties.put(grpcTypeName, grpcMaxSize);
        networkConfig.setOrdererProperties(orderername, ordererProperties);
    }

    public static String getCryptSuite(File networkConfigFile) {
        String sigAlgName = null;
        try {
            FileInputStream inputStream = new FileInputStream(networkConfigFile);
            Yaml yaml = new Yaml(new SafeConstructor());
            Map<String, Object> map = yaml.load(inputStream);
            JsonObjectBuilder builder = Json.createObjectBuilder(map);
            JsonObject jsonConfig = builder.build();
            JsonObject organizations = jsonConfig.getJsonObject("organizations");
            for (String string : organizations.keySet()) {
                JsonObject jsonObject = organizations.getJsonObject(string);
                JsonObject signedCert = jsonObject.getJsonObject("signedCert");
                String next = signedCert.keySet().iterator().next();
                if (next.equals("path")) {
                    String certPath = signedCert.getString("path");
                    X509Certificate x509Certificate = SM2Utils.getX509Certificate(certPath);
                    sigAlgName = x509Certificate.getSigAlgName();
                } else if (next.equals("pem")) {
                    String signedCertString = signedCert.getString("pem");
                    X509Certificate x509Certificate = SM2Utils.getX509Certificate(signedCertString.getBytes());
                    sigAlgName = x509Certificate.getSigAlgName();
                }
                if (sigAlgName == null) {
                    logger.info("please check the cert is correct ? Org cert : " + jsonObject.getString("mspid"));
                }
                break;
            }
            return sigAlgName;
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
        return null;
    }

    /**
     * 通过 yaml 配置文件实例化
     * @param networkConfigFile 网络配置文件
     */
    private SmChain(String channelName, File networkConfigFile, CryptoSuiteChoose cryptoSuiteChoose) {
        try {
            if (cryptoSuiteChoose == SmChain.CryptoSuiteChoose.GM) {
                this.networkConfig = NetworkConfig.fromYamlFile(networkConfigFile, true);
                this.setNetworkConfig();
                this.hfClient = HFClient.createNewInstance();
                Properties gmproperties = new Properties();
                gmproperties.setProperty("org.hyperledger.fabric.sdk.hash_algorithm", "SM3");
                gmproperties.setProperty("org.hyperledger.fabric.sdk.crypto.default_signature_algorithm", "SM3WITHSM2");
                CryptoSuite gmcryptoSuite = CryptoSuite.Factory.getCryptoSuite(gmproperties);
                this.hfClient.setCryptoSuite(gmcryptoSuite);
            } else if (cryptoSuiteChoose == SmChain.CryptoSuiteChoose.ECDSA) {
                this.networkConfig = NetworkConfig.fromYamlFile(networkConfigFile);
                this.setNetworkConfig();
                this.hfClient = HFClient.createNewInstance();
                this.hfClient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
            }

            this.hfClient.setUserContext(this.networkConfig.getPeerAdmin());
            this.channel = this.hfClient.loadChannelFromConfig(channelName, this.networkConfig);
            this.channel.initialize();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过 yaml 配置文件实例化
     * @param networkConfigFile 网络配置文件
     */
    private SmChain(String channelName, File networkConfigFile) {
        try {
            String cryptSuite = getCryptSuite(networkConfigFile);
            if (cryptSuite.equals("SM3WITHSM2")) {
                logger.info("crypto suite is gm");
                this.networkConfig = NetworkConfig.fromYamlFile(networkConfigFile, true);
                setNetworkConfig();
                // 设置国密加密套件
                Properties gmpropsIn = new Properties();
                gmpropsIn.setProperty(Config.HASH_ALGORITHM, "SM3");  // hash算法
                gmpropsIn.setProperty(Config.SIGNATURE_ALGORITHM, "SM3WITHSM2"); // 算法  对应   -> SHA256withECDSA
                CryptoSuite gmCrypto = CryptoSuiteFactory.getDefault().getCryptoSuite(gmpropsIn);
                hfClient = HFClient.createNewInstance();
                hfClient.setCryptoSuite(gmCrypto);
            } else if (cryptSuite.equals("SHA256WITHECDSA")) {
                logger.info("crypto suite is ecdsa");
                this.networkConfig = NetworkConfig.fromYamlFile(networkConfigFile);
                setNetworkConfig();
                hfClient = HFClient.createNewInstance();
                hfClient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
            } else {
                logger.info("The encryption suite is not recognized : " + cryptSuite);
            }
            hfClient.setUserContext(this.networkConfig.getPeerAdmin());
            channel = hfClient.loadChannelFromConfig(channelName, this.networkConfig);
            channel.initialize();
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
    }

    private SmChain(String channelName, File networkConfigFile, String strName, String strPwd) {
        try {
            this.networkConfig = NetworkConfig.fromYamlFile(networkConfigFile);
            NetworkConfig.CAInfo caInfo = (NetworkConfig.CAInfo)this.networkConfig.getClientOrganization().getCertificateAuthorities().get(0);
            HFCAClient hfcaClient = HFCAClient.createNewInstance(caInfo);
            Enrollment enrollment = hfcaClient.enroll(strName, strPwd);
            NetworkConfig.OrgInfo clientOrg = this.networkConfig.getClientOrganization();
            SmUser user = new SmUser();
            user.setName(strName);
            user.setEnrollment(enrollment);
            user.setMspId(clientOrg.getMspId());
            this.hfClient = HFClient.createNewInstance();
            this.hfClient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
            this.hfClient.setUserContext(user);
            this.channel = this.hfClient.loadChannelFromConfig(channelName, this.networkConfig);
            this.channel.initialize();
        } catch (Exception var10) {
        }

    }

    /**
     * 自己传参数构造 SmChain
     */
    private SmChain(String channelName, Peer peer, Orderer orderer, User user, HFClient hfClient) {
        try {
            this.hfClient = hfClient;
//            this.hfClient = HFClient.createNewInstance();
            this.hfClient.setUserContext(user);
//            this.hfClient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
            channel = this.hfClient.newChannel(channelName);
            channel.addPeer(peer);
            channel.addOrderer(orderer);
            channel.initialize();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Channel getHFChannel() {
        return channel;
    }

    HFClient getHfClient(){
        return hfClient;
    }

    public static SmChain getChain(String channelName, Peer peer, Orderer orderer, User user, HFClient hfClient) {
        return new SmChain( channelName, peer, orderer, user, hfClient);
    }

    public static SmChain getChain(String channelName, File networkConfigFile, SmChain.CryptoSuiteChoose cryptoSuiteChoose) {
        return new SmChain(channelName, networkConfigFile, cryptoSuiteChoose);
    }

    public static SmChain getChain(String channelName, File blockchainNetworkConfigFile) {
        return new SmChain(channelName, blockchainNetworkConfigFile);
    }

    public static SmChain getChain(String channelName, File networkConfigFile, String strName, String strPwd) {
        return new SmChain(channelName, networkConfigFile, strName, strPwd);
    }

    public SmTransaction getTransaction() {
        return new SmTransactionImp(hfClient, channel);
    }

    public SmChaincode getChaincode() {

        return new SmChaincodeImp(hfClient, channel);
    }

    public SmChannel getChannel() {
        return new SmChannelImp(hfClient, channel);
    }

    public SmCA getCa(File networkConfigFile, String OrgName) {
        return new SmCAImp(networkConfigFile, OrgName);
    }

    @Override
    public String toString(){
        return "test";
    }
}
